package com.mi.fums.scheduler;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.LocalDateTimeUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.io.IoUtil;
import cn.hutool.core.io.file.FileNameUtil;
import cn.hutool.core.lang.UUID;
import cn.hutool.core.util.StrUtil;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.mi.fums.domain.Study;
import com.mi.fums.properties.AttachmentProperties;
import com.mi.fums.properties.FumsProperties;
import com.mi.fums.properties.ReportProperties;
import com.pig4cloud.plugin.oss.service.OssTemplate;
import lombok.RequiredArgsConstructor;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.dcm4che3.data.Attributes;
import org.dcm4che3.data.Tag;
import org.dcm4che3.imageio.codec.Transcoder;
import org.dcm4che3.io.DicomInputStream;
import org.dcm4che3.util.Property;
import org.opencv.core.Core;
import org.opencv.osgi.OpenCVNativeLoader;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Component;
import org.springframework.web.client.RestTemplate;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.InputStream;
import java.nio.file.Files;
import java.time.LocalDate;
import java.util.Comparator;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;

/**
 * <p>
 * File upload task
 * </p>
 *
 * @author jin_huaquan
 * @version 1.0
 * @date 2023/11/25 10:59
 */
@Component
@Slf4j
@RequiredArgsConstructor
public class FileUploadTask {

    private final ObjectMapper objectMapper;
    private final RestTemplate restTemplate;
    private final OssTemplate ossTemplate;
    private final AttachmentProperties attachmentProperties;
    private final FumsProperties fumsProperties;
    private final ReportProperties reportProperties;
    private final ThreadPoolTaskExecutor globalExecutor;

    public static final Property[] DCM_COMPRESS_PARAMS = new Property[]{new Property("compressionQuality", 0.5D)};
    public static final String OBJ_NAME_PATTERN = "{}/{}_77/{}/{}/{}";

    public static final String FILE_TYPE_DCM = "dcm";
    public static final String userAgent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.45 Safari/537.36";

    /**
     * 1.遍历目录
     * 2.上传OSS
     * 3.上报文件信息
     */
    @Scheduled(fixedDelay = 1000)
    public void uploadFile() {
        uploadFile(attachmentProperties.getPath());
    }

    @Scheduled(fixedDelay = 1000)
    public void retryUploadFile() {
        uploadFile(attachmentProperties.getErrPath());
    }


    private void uploadFile(String filePath) {
        List<File> files = FileUtil.loopFiles(filePath, file -> file.getTotalSpace() != 0);
        if (CollUtil.isEmpty(files)) {
            return;
        }

        String executionId = UUID.fastUUID().toString();
        log.info("Start upload file. Execution id: {},  Start time: {}, file count: {}", executionId, LocalDateTimeUtil.now(), files.size());

        files.sort(Comparator.comparing(File::lastModified));
        List<Study> studies = files.parallelStream()
                .map(this::uploadOss)
                .filter(Objects::nonNull)
                .collect(Collectors.toList());

        log.info("End upload file.Execution id: {}, End time: {}, Upload count: {}", executionId, LocalDateTimeUtil.now(), studies.size());

        globalExecutor.execute(() -> CollUtil.split(studies, 500).forEach(this::reportStudyRecords));
    }


    /**
     * File oss upload
     *
     * @param file file
     * @return DCM study info
     */
    private Study uploadOss(File file) {
        String fileName = file.getName();
        Attributes attributes = null;
        //ByteArrayInputStream stream = new ByteArrayInputStream(IoUtil.readBytes(FileUtil.getInputStream(file)));
        InputStream stream = null;
        Study study = null;
        try (DicomInputStream in = new DicomInputStream(file)) {
            attributes = in.readDataset(-1, -1);
            //stream.reset();
            stream = FileUtil.getInputStream(file);
            log.info("File compress success!. File name: {}.", fileName);
        } catch (Exception e) {
            log.error("Compress dcm occurs error: {}. File name: {}", e.getMessage(), fileName);

            return null;
        }

        try {
            study = getStudy(attributes);
            study.setFileName(fileName);
            String objName = StrUtil.format(OBJ_NAME_PATTERN, attachmentProperties.getUploadPath(), fumsProperties.getHoscode(), LocalDate.now().format(DatePattern.PURE_DATE_FORMATTER), study.getStudyUid(), fileName);
            ossTemplate.putObject(StrUtil.EMPTY, objName, stream);

            log.info("File upload oss success! File name: {}, Obj name: {}", fileName, objName);
        } catch (Exception e) {
            log.error("File upload oss failed! File name: {}, Error msg: {}", fileName, e.getMessage());

            // 移动到特定的错误目录下，另外的任务重新上传
            Study finalStudy = study;
            globalExecutor.execute(() -> {
                File targetFile = new File(StrUtil.join(File.separator, attachmentProperties.getErrPath(), LocalDate.now(), finalStudy.getStudyUid()), fileName);
                FileUtil.move(file, targetFile, true);
            });

            return null;
        }

        Study finalStudy = study;
        globalExecutor.execute(() -> {
            File targetFile = new File(StrUtil.join(File.separator, attachmentProperties.getMvPath(), LocalDate.now(), finalStudy.getStudyUid()), fileName);
            FileUtil.move(file, targetFile, true);
        });

        return study;
    }


    /**
     *  会缺tag 240105
     *
     * @param file file
     * @return DCM study info
     */

    private Study uploadOssv2(File file) {
        String fileName = file.getName();
        Attributes attributes = null;
        ByteArrayInputStream stream = new ByteArrayInputStream(IoUtil.readBytes(FileUtil.getInputStream(file)));
        Study study = null;
        try (DicomInputStream in = new DicomInputStream(stream)) {
            attributes = in.readDataset(-1, -1);
            stream.reset();
            log.info("File compress success!. File name: {}.", fileName);
        } catch (Exception e) {
            log.error("Compress dcm occurs error: {}. File name: {}", e.getMessage(), fileName);

            return null;
        }

        try {
            study = getStudy(attributes);
            study.setFileName(fileName);
            String objName = StrUtil.format(OBJ_NAME_PATTERN, attachmentProperties.getUploadPath(), fumsProperties.getHoscode(), LocalDate.now().format(DatePattern.PURE_DATE_FORMATTER), study.getStudyUid(), fileName);
            ossTemplate.putObject(StrUtil.EMPTY, objName, stream);

            log.info("File upload oss success! File name: {}, Obj name: {}", fileName, objName);
        } catch (Exception e) {
            log.error("File upload oss failed! File name: {}, Error msg: {}", fileName, e.getMessage());

            // 移动到特定的错误目录下，另外的任务重新上传
            Study finalStudy = study;
            globalExecutor.execute(() -> {
                File targetFile = new File(StrUtil.join(File.separator, attachmentProperties.getErrPath(), LocalDate.now(), finalStudy.getStudyUid()), fileName);
                FileUtil.move(file, targetFile, true);
            });

            return null;
        }

        Study finalStudy = study;
        globalExecutor.execute(() -> {
            File targetFile = new File(StrUtil.join(File.separator, attachmentProperties.getMvPath(), LocalDate.now(), finalStudy.getStudyUid()), fileName);
            FileUtil.move(file, targetFile, true);
        });

        return study;
    }


    /**
     * 根据DCM文件的tag信息拼接study信息
     *
     * @param dataset 文件tag属性集
     * @param
     */
    private Study getStudy(Attributes dataset) {
        Study study = new Study();

        String studyDate = dataset.getString(Tag.StudyDate);
        String studyTime = dataset.getString(Tag.StudyTime);

        study.setStudyUid(dataset.getString(Tag.StudyInstanceUID));
        study.setSopUid(dataset.getString(Tag.SOPInstanceUID));
        study.setSeriesInstanceUid(dataset.getString(Tag.SeriesInstanceUID));
        study.setBodyPartExamined(dataset.getString(Tag.BodyPartExamined));
        //study.setInstitutionName(dataset.getString(Tag.InstitutionName));
        study.setPatientId(dataset.getString(Tag.PatientID));
        study.setPatientName(dataset.getString(Tag.PatientName));
        study.setPatientSex(dataset.getString(Tag.PatientSex));
        study.setPatientAge(dataset.getString(Tag.PatientAge));
        study.setStudyDate(studyDate);
        // 防止有的设备时间格式不一样
        //study.setStudyDateTime(LocalDateTimeUtil.parse(studyDate + studyTime, DatePattern.PURE_DATETIME_PATTERN).format(DatePattern.NORM_DATETIME_FORMATTER));
        study.setStudyDateTime(studyDate);

        study.setModality(dataset.getString(Tag.Modality));
        study.setDicomCount(0);
        study.setInstanceNumber(dataset.getString(Tag.InstanceNumber));
        study.setSeriesNumber(dataset.getString(Tag.SeriesNumber));
        // FIXME 从配置文件中读取
        study.setHoscode(fumsProperties.getHoscode());

        return study;
    }

    @SneakyThrows(JsonProcessingException.class)
    private void reportStudyRecords(List<Study> studies) {
        HttpHeaders headers = new HttpHeaders();
        // 设置请求媒体数据类型
        headers.setContentType(MediaType.APPLICATION_JSON);
        headers.add(HttpHeaders.USER_AGENT, userAgent);

        HttpEntity<String> requestEntity = new HttpEntity<>(objectMapper.writeValueAsString(studies), headers);
        String result = restTemplate.postForObject(reportProperties.getEndpoint(), requestEntity, String.class);
        log.info("Study records upload success! Resp: {}", result);
    }
}
